﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using RouterControl.Whs.Wizards;
using Microsoft.HomeServer.Controls;
using RouterControl.IO.UPnP;
using System.Runtime.InteropServices;
using RouterControl.IO;

namespace RouterControl.Diag
{
    public partial class PageSearch : WizardPageClientBannerTop
    {
        private readonly log4net.ILog _log = log4net.LogManager.GetLogger(typeof(PageSearch));
        private bool _failed = false;

        private WizardMain ParentWizardMain
        {
            get { return (WizardMain)this.ParentWizard; }
        }

        public PageSearch()
        {
            InitializeComponent();
        }

        private void PageSearch_BackButtonClick(object sender, EventArgs e)
        {
            this.SetCurrentPage<PagePrepare>();
        }

        public override void StartRealWork()
        {
            base.StartRealWork();

            // continue
            this.labelResult.Text = Properties.Resources.txtPleaseWait;

            this.searchBgWorker.RunWorkerAsync();
        }

        private void searchBgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            _log.Info("Searching for routers natively...");

            try
            {
                var finder = new UPnPDeviceFinderClass();

                var devices = finder.FindByType(IO.RootDeviceTypeIds.InternetGatewayDeviceType, 0);

                _log.InfoFormat("Found {0} routers (native)", devices.Count);

                // no device found
                if (devices.Count <= 0)
                {
                    _failed = true;

                    this.ParentWizardMain.Errors.Add(new WizardMain.ErrorInfo()
                    {
                        Severity = WizardMain.Severity.Error,
                        Title = Properties.Resources.txtNoRouterFoundTitle,
                        Text = Properties.Resources.txtNoRouterFoundDesc,

                    });

                    return;
                }

                // inform the user
                this.labelResult.Text = string.Format(
                    Properties.Resources.txtDevicesFound,
                    devices.Count
                    );

                // handle devices
                var devicesOk = new List<IUPnPDevice>();

                foreach (IUPnPDevice d in devices)
                {
                    // dump common
                    _log.InfoFormat("Found Device {0} ({1})", d.FriendlyName, d.UniqueDeviceName);

                    LogProperties(d);

                    try
                    {
                        // manually download device description
                        _log.InfoFormat("Downloading description for {0} ({1})", d.FriendlyName, d.UniqueDeviceName);

                        var ds = new DeviceSchema(d);

                        ds.DownloadDeviceDescription();

                        // log schema
                        FilesToZip.Instance.AddFile(new DiagFile("DeviceSchema_" + ds.SchemaId + ".native.xml", ds.ToXml()));

                        // manually compile device
                        _log.InfoFormat("Compiling Device {0} ({1})", d.FriendlyName, d.UniqueDeviceName);

                        var dom = ds.GetDeviceSchemaDom();

                        // log device DOM
                        FilesToZip.Instance.AddFile(new DiagFile("DeviceDom_" + ds.SchemaId + ".native.xml", dom.ToXml()));

                        // explicitly check enumeration
                        foreach (var s in d.Services) ;

                        // this device is okay
                        devicesOk.Add(d);
                    }
                    catch (COMException ex)
                    {
                        var err = (uint)ex.ErrorCode;

                        if ((err == (uint)DeviceHelpers.COMExceptionErrorCode.UPNP_E_INVALID_DOCUMENT) ||
                            (err == (uint)DeviceHelpers.COMExceptionErrorCode.UPNP_E_EVENT_SUBSCRIPTION_FAILED))
                        {
                            _log.Error(
                                "Device does not implement UPNP correctly",
                                ex
                                );

                            _failed = true;

                            this.ParentWizardMain.Errors.Add(new WizardMain.ErrorInfo()
                            {
                                Severity = WizardMain.Severity.Error,
                                Title = Properties.Resources.txtBrokenUpnpImplementationTitle,
                                Text = string.Format(
                                    Properties.Resources.txtBrokenUpnpImplementationDesc,
                                    d.FriendlyName
                                    ),
                                Error = ex

                            });

                            return;
                        }

                        // unknown error
                        throw;
                    }
                }

                // finalize
                this.ParentWizardMain.FoundDevices = devicesOk;
            }
            catch (Exception ex)
            {
                _log.Error("Failed to find routers natively", ex);

                _failed = true;

                this.ParentWizardMain.Errors.Add(new WizardMain.ErrorInfo()
                {
                    Severity = WizardMain.Severity.Error,
                    Title = Properties.Resources.txtUpnpSearchFailedTitle,
                    Text = Properties.Resources.txtUpnpSearchFailedDesc,
                    Error = ex

                });
            }
        }

        private void searchBgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (_failed)
                this.SetCurrentPage<PageShowErrors>();
            else
                this.SetCurrentPage<PageSelectRouter>();
        }

        private void LogProperties(object o)
        {
            if (o == null)
                throw new ArgumentNullException("o");

            foreach (var p in o.GetType().GetProperties())
                _log.DebugFormat("... {0} = \"{1}\"", p.Name, p.GetValue(o, null));
        }
    }
}
